package mysql8学习.高级.第15章_锁;

/**
 * 2. MySQL并发事务访问相同记录
 * 并发事务访问相同记录的情况大致可以划分为3种:
 *
 * 2.1 读-读 情况   没任何问题
 * 读-读情况，    即并发事务相继读取相同的记录。
 * 读取操作本身不会对记录有任何影响，并不会引起什么问题，所以允许这种情况的发生。
 *
 * 2.2 写-写 情况
 * 写-写情况，   即并发事务相继对相同的记录做出改动。
 * 在这种情况下会发生脏写的问题，任何一种隔离级别都不允许这种问题的发生。
 * 当某个事务要对某条纪律执行时，会生成一个锁结构，
 * 在锁结构里有很多信息，为了简化理解， 只把两个比较重要的属性拿了出来:
 * ● trx信息:代表这个锁结构是哪个事务生成的。
 * ● is.waiting: 代表当前事务是否在等待。
 *               最初是 false 表示没事务占用
 *
 * 2.3读-写或写-读情况
 * 读-写或写-读，即一个事务进行读取操作，另一个进行改动操作。
 * 这种情况下可能发生脏读、不可重复读、幻读的问题。
 * 各个数据库厂商对SQL标准的支持都可能不一样。
 * 比如MySQL在REPEATABLE READ 隔离级别 上就已经解决了幻读问题。
 *
 * 2.4并发问题的解决方案
 * 怎么解决脏读、不可重复读、 幻读这些问题呢?其实有两种可选的解决方案:
 *  ●方案一:读操作利用多版本并发控制(MVCC， 下章讲解)，写操作进行加锁。
 *      所谓的MVCC，就是生成一个ReadView，
 *      通过ReadView找到符合条件的记录版本(历史版本由undo日志构建)。
 *      查询语句只能读到在生成ReadView之前已提交事务所做的更改，
 *      在生成ReadView之前未提交的事务或者之后才开启的事务所做的更改是看不到的。
 *      而写操作肯定针对的是最新版本的记录，读记录的历史版本和改动记录
 *      的最新版本本身并不冲突，也就是采用MVCC时，读-写 操作并不冲突。
 * ●方案二:读、写操作都采用加锁的方式。
 *      如果我们的一些业务场景不允许读取记录的旧版本,而是每次都必须去读取记录的最新版本。
 *      比如，在银行存款的事务中，你需要先把账户的余额读出来，然后将其加上本次存款的数额，
 *      最后再写到数据库中。
 *      在将账户余额读取出来后，就不想让别的事务再访问该余额，直到本次存款事务执行完成，
 *      其他事务才可以访问账户的余额。
 *      这样在读取记录的时候就需要对其进行加锁操作,
 *      这样也就意味着读操作和写操作也像写-写操作那样排队执
 *      脏读和不可重复读都可以再加锁的情况下避免，
 *      因为操作的都是同一数据但是幻读是插入，无法进行对不存在的数据加锁，所以不可避免
 *
 *     脏读 的产生是因为当前事务读取了另一个未提交事务写的一条记录，
 *      如果另一个事务在写记录的时候就给这条记录加锁，那么当前事务就无法继续读取该记录了,
 *      所以也就不会有脏读问题的产生了.
 *
 *     不可重复读 的产生是因为当前事务先读取一条记录，另外一个事务对该记录做了改动之后并提交之后,
 *      当前事务再次读取时会获得不同的值，如果在当前事务读取记录时就给该记录加锁，
 *      那么另一个事务就无法修改该记录,自然也不会发生不可重复读了。
 *
 *     幻读 问题的产生是因为当前事务读取了一个范围的记录,然后另外的事务向该范围内插入了新记录,
 *      当前事务再次读取该范围的记录时发现了新插入的新记录。
 *      采用加锁的方式解决幻读问题就有一些麻烦，
 *      因为当前事务在第一次读取记录时幻影记录并不存在，
 *      所以读取的时候加锁就有点尴尬(因为你并不知道给谁加锁)。
 *
 * ●小结对比发现:
 *      。采用MVCC方式的话，读-写操作彼此并不冲突，性能更高。
 *      。采用加锁方式的话， 读-写操作彼此需要排队执行，影响性能。
 *      一般情况下我们当然愿意采用MVCC来解决读-写操作并发执行的问题,但是业务在某些特殊情况下，
 *      要求必须采用加锁的方式执行。下面就讲解下MySQL中不同类别的锁。
 *
 *
 *
 */
public class B_MySQL并发事务访问相同记录 {
}
